package com.xuecheng.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.xuecheng.commons.enums.ErrorCode;
import com.xuecheng.commons.model.dto.CoursePubIndexDto;
import com.xuecheng.commons.model.dto.PageRequest;
import com.xuecheng.commons.model.vo.CoursePubIndexVo;
import com.xuecheng.commons.model.vo.CoursePubVo;
import com.xuecheng.commons.model.vo.PageResponseResult;
import com.xuecheng.commons.model.vo.ResponseResult;
import com.xuecheng.commons.utils.BeanHelper;
import com.xuecheng.content.api.ContentClient;
import com.xuecheng.search.domain.CoursePubIndex;
import com.xuecheng.search.service.SearchService;
import com.xuecheng.web.exception.BusinessException;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author Sober_wxy
 * @date 2022/7/25 16:07
 */
@Service
public class SearchServiceImpl implements SearchService {

        @Autowired
        private RestHighLevelClient restHighLevelClient;

        @Autowired
        private ContentClient contentClient;

        @Override
        public ResponseResult search(PageRequest pageRequest, CoursePubIndexDto dto) throws IOException {
                // 创建SearchRequest
                SearchRequest searchRequest = new SearchRequest("course");
                // 构建SearchSourceBuilder对象传递分页 高亮 过滤等条件
                searchRequest.source(generatorBuilder(pageRequest, dto));
                // 调用RestClient 发起请求 返回响应
                SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
                // 解析响应返回
                return parseResponse(response);
        }

        /**
         * 设置查询参数
         *
         * @param pageRequest 分页参数
         * @param dto         查询参数对象
         * @return SearchSourceBuilder
         */
        private SearchSourceBuilder generatorBuilder(PageRequest pageRequest, CoursePubIndexDto dto) {
                // 创建SearchSourceBuilder对象
                SearchSourceBuilder builder = new SearchSourceBuilder();
                // 设置查询条件
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                if (StringUtils.isEmpty(dto.getKeyword())) {
                        // 用户搜索条件为空
                        boolQuery.must(QueryBuilders.matchAllQuery());
                } else {
                        // 不为空
                        boolQuery.must(QueryBuilders.matchQuery("all", dto.getKeyword()));
                }
                builder.query(boolQuery);
                // 设置分页
                builder.from((pageRequest.getPageNo() - 1) * pageRequest.getPageSize());
                builder.size(pageRequest.getPageSize());
                // 设置条件
                // 一级分类
                if (!StringUtils.isEmpty(dto.getMt())) {
                        boolQuery.filter(QueryBuilders.termQuery("mt", dto.getMt()));
                }
                // 二级分类
                if (!StringUtils.isEmpty(dto.getSt())) {
                        boolQuery.filter(QueryBuilders.termQuery("st", dto.getSt()));
                }
                // 难度分类
                if (!StringUtils.isEmpty(dto.getGrade())) {
                        boolQuery.filter(QueryBuilders.termQuery("grade", dto.getGrade()));
                }
                // 设置高亮
                HighlightBuilder highlightBuilder = new HighlightBuilder();
                HighlightBuilder hl = highlightBuilder.preTags("<font color=red>")
                        .postTags("</font>")
                        .field("name")
                        .requireFieldMatch(false);
                builder.highlighter(hl);
                return builder;
        }

        /**
         * 解析查询的响应结果
         *
         * @param response json
         * @return PageResponseResult
         */
        private ResponseResult parseResponse(SearchResponse response) {
                // 获取记录总数
                SearchHits hits = response.getHits();
                long total = hits.getTotalHits().value;
                // 获取查询到的内容
                SearchHit[] searchHits = hits.getHits();
                List<CoursePubIndexVo> vos = new ArrayList<>();
                // 遍历
                for (SearchHit searchHit : searchHits) {
                        // 获取每个hit的source数据
                        String json = searchHit.getSourceAsString();
                        // 转化为java对象
                        CoursePubIndex index = JSON.parseObject(json, CoursePubIndex.class);
                        // 转换为vo对象
                        CoursePubIndexVo vo = BeanHelper.copyProperties(index, CoursePubIndexVo.class);
                        // 单独设置vo中的index参数
                        vo.setIndexId(index.getId());
                        // 解析高亮部分的数据
                        Map<String, HighlightField> map = searchHit.getHighlightFields();
                        if (map != null && map.containsKey("name")) {
                                // 根据name属性 显示高亮内容
                                HighlightField highlightField = map.get("name");
                                String name = highlightField.getFragments()[0].toString();
                                vo.setName(name);
                        }
                        vos.add(vo);
                }
                return PageResponseResult.okResult(total, vos);
        }

        /**
         * 同步es索引
         *
         * @param coursePubId 发布的课程id
         */
        @Override
        public void createIndex(Long coursePubId) throws IOException {
                // 根据id 查询课程发布对象
                ResponseResult<CoursePubVo> result = contentClient.findById(coursePubId);
                CoursePubVo coursePubVo = result.getData();
                if (coursePubVo != null) {
                        // 调用es的api保存数据
                        IndexRequest request = new IndexRequest("course");
                        // 设置id
                        request.id(coursePubVo.getId().toString());
                        // 设置数据
                        request.source(JSON.toJSONString(coursePubVo), XContentType.JSON);
                        // 调用restClient保存
                        restHighLevelClient.index(request, RequestOptions.DEFAULT);
                }
        }

        /**
         * 根据id查询es的数据
         *
         * @param id 课程发布id
         */
        @Override
        public ResponseResult findById(Long id) throws IOException {
                // 创建根据id查询的请求对象
                GetRequest request = new GetRequest("course");
                request.id(id.toString());
                // 发送请求
                GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT);
                // 解析json 转换对应的java对象
                String json = response.getSourceAsString();
                CoursePubIndex index = JSON.parseObject(json, CoursePubIndex.class);
                // 转换vo
                CoursePubIndexVo vo = BeanHelper.copyProperties(index, CoursePubIndexVo.class);
                // 设置index的id
                vo.setIndexId(index.getId());
                return ResponseResult.okResult(vo);
        }

        /**
         * 根据 id 删除 es索引
         *
         * @param coursePubId 发布的课程id
         */
        @Override
        public void deleteIndex(Long coursePubId) throws IOException {
                if (coursePubId == null){
                        throw new BusinessException(ErrorCode.PARAMSERROR);
                }
                // 调用es的api保存数据
                DeleteRequest deleteRequest = new DeleteRequest("course", coursePubId.toString());
                // 调用restClient删除
                restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        }
}
